#include <iostream>
#include <string>
#include <functional>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>

typedef websocketpp::server<websocketpp::config::asio> wsserver_t;

void http_callback(wsserver_t *svr, websocketpp::connection_hdl hdl)
{
    // 通过server获取connection
    wsserver_t::connection_ptr conn = svr->get_con_from_hdl(hdl);

    // 获取请求正文
    std::cout << "body: " << conn->get_request_body() << std::endl;

    // 获取http请求对象
    websocketpp::http::parser::request req = conn->get_request();

    // 获取请求正文
    std::cout << "body: " << req.get_body() << std::endl;

    // 获取请求方法
    std::cout << "method: " << req.get_method() << std::endl;

    // 获取请求uri接口
    std::cout << "uri: " << req.get_uri() << std::endl;

    // 进行响应，给客户端返回一个hello world页面
    std::string body = "<html><body><h1>Hello World!</h1></body></html>";
    conn->set_body(body);                                   // 响应正文
    conn->append_header("Content-Type", "text/html");       // 响应头
    conn->set_status(websocketpp::http::status_code::ok);   // 响应状态码

}

void wsopen_callback(wsserver_t *svr, websocketpp::connection_hdl hdl)
{
    std::cout << "websocket握手成功!" << std::endl;
}

void wsclose_callback(wsserver_t *svr, websocketpp::connection_hdl hdl)
{
    std::cout << "websocket断开连接成功!" << std::endl;
}

// websocket连接收到消息的回调函数
void wsmessage_callback(wsserver_t *svr, websocketpp::connection_hdl hdl, wsserver_t::message_ptr msg)
{
    wsserver_t::connection_ptr conn = svr->get_con_from_hdl(hdl);

    // 输出消息
    std::cout << "wsmsg: " << msg->get_payload() << std::endl;

    // 进行响应
    std::string resp = "client say: " + msg->get_payload();
    conn->send(resp, websocketpp::frame::opcode::text);
}

int main()
{
    // 1. 实例化服务器对象
    wsserver_t wssvr;

    // 2. 设置websocketpp库的⽇志级别
    // all表⽰打印全部级别⽇志
    // none表⽰什么⽇志都不打印
    // wssvr.set_access_channels(websocketpp::log::alevel::all);
    wssvr.set_access_channels(websocketpp::log::alevel::none);

    // 3. 初始化asio异步IO调度器
    wssvr.init_asio();

    // 4. 设置地址复用(之前tcp套接字bind函数重启不能立即绑定原来的端口号)
    wssvr.set_reuse_addr(true);

    // 5. 设置WebSocket服务器的各种事件处理函数
    // WebSocketpp库是这么定义的：
    // typedef lib::function<void(connection_hdl)> http_handler;
    // void set_http_handler(http_handler h);

    // 这几个回调函数未来其实是由服务器自己去调用的，而不是我们来调用
    // 当服务器收到http请求，则服务器就会自动调用我们所实现的http_handler类型的回调函数
    // 当服务器收到websocket握手的请求，则在握手建立好之后会调用我们所实现的open_handler类型的回调函数,其他两个类型也是如此
    // 但在回调函数中，我们想用服务器类里面的某些接口来实现简单的业务处理，所以我们希望把wssvr对象也传到四个回调函数里面
    // 而此时的做法就是通过bind来绑定部分参数，其余服务器自己调用时传递的参数我们通过占位符给预留出来，让服务器自己去传参，我们不操这个心，
    wssvr.set_http_handler(std::bind(&http_callback, &wssvr, std::placeholders::_1));
    wssvr.set_open_handler(std::bind(&wsopen_callback, &wssvr, std::placeholders::_1));
    wssvr.set_close_handler(std::bind(&wsclose_callback, &wssvr, std::placeholders::_1));
    wssvr.set_message_handler(std::bind(&wsmessage_callback, &wssvr, std::placeholders::_1, std::placeholders::_2));

    // 6. 设置服务器绑定的监听端口
    wssvr.listen(8080);

    // 7. 获取内核监听队列中已经完成三次握手的连接，开始通信
    wssvr.start_accept();

    // 8. 启动服务器
    wssvr.run();

    return 0;
}
